#include "kmeans_def.hpp"
#include "kmeans_impl.hpp"

#include "kkmeans_def.hpp"
#include "kkmeans_impl.hpp"

#include "opt_kkmeans_def.hpp" // Hoping this is faster that previous version
#include "opt_kkmeans_impl.hpp"

#include "Grass_kernels_def.hpp"
#include "Grass_kernels_impl.hpp"

inline	
clustering_grass::clustering_grass()
{
  test_vecdata();
}

//esto lo voy a hacer desde afuera. Se quitara de aca
inline	
clustering_grass::clustering_grass( std::string in_Gpointsfile )
:Gpointsfile(in_Gpointsfile)
{
  
}



///Using Kernel Kmeans - Method to be accessed form the main
//save_ker: to save or load the Kernel
//save_part: to save the partitions--Maybe I don't need it any more
//GT: load the indices of the Ground Truth
inline	
void
clustering_grass::ker_cluster(std::string save_ker, std::string save_part, std::string GT, int summ_percentage)  //Using Kernel Kmeans
{
   grass_points.load(Gpointsfile);
   grass_kernels G_ker(grass_points);
  
   
   
   Ncent = grass_points.n_rows *summ_percentage/100;
   Ncent = 10;
   cout << "Ncent " << Ncent << endl;
   
   
  //mat Kp = G_ker.projection2();
  //cout << "Saving Kp" << endl;
  //Kp.save(save_ker,raw_ascii);
  //getchar();
  //cout << "Distance for Projection Kernel" << endl;
  //mat dist_Kp = cal_dist_ker(Kp);
  
  mat Kp;
  cout << "Loading Kp" << endl;
  Kp.load(save_ker,raw_ascii);
  
  
  ///De aca para adelante cambia
  
  uword max_iter = 100; //INPUT???
  
  //run_kkmeans(Kp, max_iter, Ncent); //labels_actions calculated in calc_kernel_grass
  //cout << "Opt_Kkmeans" << endl;
  //opt_run_kkmeans(Kp, max_iter,  Ncent, save_part, GT);
  
  cout << "Kernel kmeans" << endl;
  run_kkmeans(Kp, max_iter,  Ncent, save_part, GT);
 
 
}

///Using Euclidean Kmeans (spectral clustering???) - Method to be accessed form the main
//save_ker: to save or load the Kernel
//save_part: to save the partitions--Maybe I don't need it any more
//GT: load the indices of the Ground Truth
inline	
void
clustering_grass::euc_cluster(std::string save_ker, std::string save_part, std::string GT, int summ_percentage) //Using Euclidean K-means: This doesn't work. I have no idea why . Oct 29/2013 : Working :)
{
  //Grassmann
  //field<mat> grass_points_tmp;
  grass_points.load(Gpointsfile);
  //grass_points = grass_points_tmp.rows( 0,  grass_points_tmp.n_elem - 2);
  
  Ncent = grass_points.n_rows *summ_percentage/100;
  //Ncent = 50;

  cout << "Ncent " << Ncent << endl;
  
  //grass_kernels G_ker(grass_points);
  //mat Kp = G_ker.projection2();
  //cout << "Saving Kp" << endl;
  //Kp.save(save_ker,raw_ascii);
  //getchar();
  //cout << "Distance for Projection Kernel" << endl;
  //mat dist_Kp = cal_dist_ker(Kp);
  
  mat Kp;
  cout << "Loading Kp" << endl;
  Kp.load(save_ker,raw_ascii);
  
  
  uword max_iter = 100; //INPUT???
  
  
  mat new_data = new_points(Kp, Ncent); 
  run_kmeans(new_data, max_iter, Ncent, save_part, GT );
  //getchar();
}



inline 
mat
clustering_grass::new_points(mat K, int m) // Sareh's paper: Clustering on Grassmann manifolds... (Fig. 1)
{
  //K.print("K:");
  rowvec d = sum(K);
  //d.print("D");
  rowvec sd = sqrt(d);
  mat Kn = inv(diagmat(sd))*K*inv(diagmat(sd));//normalised Kernel
  //Kn.print("Kn:");
  
  vec eigval;
  mat eigvec;
  eig_sym(eigval, eigvec, Kn);        
  //eigvec.print("eigvec");
  //cout << "m = " << m << endl;
  //cout << "eigvec.n_cols " << eigvec.n_cols << endl;
  //cout << "eigvec.n_rows " << eigvec.n_rows << endl;
  int num_vec = eigvec.n_cols;
  //eigvec.print("eigvec:");
  //cout << "num_vec- m: " << num_vec-m << endl;
  mat U = eigvec.cols(num_vec - m , num_vec-1);
  //U.print("U");
  
  mat data = U.t();
  //data.print("data"); 
  return data;
}




///Kernel Kmeans
inline	
void
clustering_grass::run_kkmeans(mat K, uword max_iter, uword Ncent, std::string part_name, std::string GT)
{ 
  //wall_clock timer;
  //timer.tic();
  //cout << Kernel Kmeans: " << endl;  
  //cout << "Ncent: " << Ncent << endl;
  
  k_kmeans kkm(K, Ncent);
  kkm.calc(max_iter);
  
  umat partitions = kkm.get_partitions();
  partitions.save(part_name, raw_ascii);
//   uvec ind_summ = kkm.get_id_summ();
//   ind_summ.t().print("final_summary in Grassmann Points.. press a key");
//   getchar();
  
  uvec nearpoints = kkm.near_points();
  nearpoints.t().print("final_summary in Grassmann Points.. press a key");
  //getchar();
  
  //cout << "Number of points in small clusters: " << ind_summ.n_elem << endl;
  //op_kkm.print_labels(labels);
  //kkm.save_labels("X_lab_fin.dat");
  
  //double n_secs = timer.toc();
  //cout << "took " << setprecision(15) << n_secs << " seconds" << endl;
  
  
  vec gt_ind;
  gt_ind.load(GT);
  
    
}


///Opt Kernel Kmeans
inline	
void
clustering_grass::opt_run_kkmeans(mat K, uword max_iter, uword Ncent, std::string part_name, std::string GT)
{ 
  //wall_clock timer;
  //timer.tic();
  cout << "opt_Kernel Kmeans: " << endl;  
  //cout << "Ncent: " << Ncent << endl;
  
  op_kkmeans op_kkm(K, Ncent);
  op_kkm.calc(max_iter);
  
}

///Euclidean Kmeans
inline	
void
clustering_grass::run_kmeans(mat new_data, uword max_iter, uword Ncent, std::string part_name, std::string GT)
{ 
  //cout << "Kmeans: " << endl;  
  //cout << "Ncent: " << Ncent << endl;
  
  kmeans km(new_data, Ncent);
  km.run(max_iter);
  //umat partitions = km.get_partitions();
  //partitions.save(part_name, raw_ascii);
  //uvec ind_summ = km.get_id_summ();
  //ind_summ.t().print("final_summary in Grassmann Points.. press a key");
  
  cout << "Retrieving summary" << endl;
  uvec nearpoints = km.near_points();
  nearpoints.t().print("final_summary in Grassmann Points.. press a key");
  
 
  
  
  vec gt_ind;
  gt_ind.load(GT);
  
  
  //gt_ind.print("gt_ind:");
  double  mat = 0;
  
  for (uword i = 0; i < nearpoints.n_elem; ++i )
  {
    uword Gn = nearpoints(i);    
    //cout << "Gn= " << Gn << endl;

    //uvec q1 = find( gt_ind == Gn );
    double check  = gt_ind (Gn);
    
    if (check==1)
    {
        mat++;
    }
  }
  
  cout << "My summary has: " << nearpoints.n_elem << " Grassmann Points. " << mat << " match with GT" << endl;
  //cout << "The annormal activity is in " << gt_ind.n_elem << " frames"<<endl;
  
}


/// cal_dist_ker()
inline 
mat
clustering_grass::cal_dist_ker(mat K)
{
  mat dist(K.n_rows , K.n_cols);
  
  for (uword i = 0; i < K.n_rows; ++i)
    for (uword j= 0; j < K.n_cols; ++j)
    {
      dist(i,j) = K(i,i) - 2*K(i,j) + K(j,j);
    }
    
    //dist.diag().print("dist_diag");
  return dist;
  //DIST.print("DIST");
  //DIST.save("DIST.mat",raw_ascii);
  
}


inline 
uvec
clustering_grass::get_performance()
{
  return performance;
  
}





//**************************************************************************************
//**************************************************************************************
//**************************************************************************************
///For test in Vectorial Data
inline	
void
clustering_grass::test_vecdata()
{
  //Testing with Vec data
  cout << "Testing with Vec data" << endl;
  cout << "X loaded from folder" << endl;
  mat vec_data;
  vec_data.load("X.dat",raw_ascii);
  
  mat K = calc_kernel_test(vec_data);
  field<std::string> lab_X;
  lab_X.load("orig_labels_X.txt");
  uword max_iter = 10; //INPUT???
  //run_kkmeans(K, max_iter, 2); as run_kkmeans, it must changed as well
  
  //Using Euclidean K-means: This doesn't work. I have no idea why
  //mat new_points = new_points(K, m); 
  //run_kmeans(new_points, max_iter, lab_X, 2);
}

///For test in Vectorial Data
inline	
mat
clustering_grass::calc_kernel_test(mat vec_data) // Normalised - for vectorial data.
{
  
  //vec_data = vec_data.t();
  cout << "vec_data cols " << vec_data.n_cols << endl;
  cout << "vec_data rows " << vec_data.n_rows << endl;
  
  uword N_vec = vec_data.n_cols;
  mat K = zeros( N_vec , N_vec );
  K = pow( vec_data.t()*vec_data, 2); // Polynomial d = 2.
  return K;
}
